{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "cathedral-skating",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import time\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import numpy as np\n",
    "import scipy.sparse as sp\n",
    "from torch.nn.modules.module import Module\n",
    "from torch.nn.parameter import Parameter\n",
    "\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "hidden = 16                                     # 定义隐藏层数\n",
    "dropout = 0.5\n",
    "lr = 0.01 \n",
    "weight_decay = 5e-4\n",
    "fastmode = 'store_true'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "fabulous-nutrition",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading cora dataset...\n"
     ]
    },
    {
     "ename": "PermissionError",
     "evalue": "[Errno 13] Permission denied: './cora/'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mPermissionError\u001b[0m                           Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-4-ef7b8cf6d680>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     54\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     55\u001b[0m     \u001b[1;32mreturn\u001b[0m \u001b[0madj\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfeatures\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0midx_train\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0midx_val\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0midx_test\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 56\u001b[1;33m \u001b[0madj\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfeatures\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlabels\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0midx_train\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0midx_val\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0midx_test\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mload_data\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-4-ef7b8cf6d680>\u001b[0m in \u001b[0;36mload_data\u001b[1;34m(path, dataset)\u001b[0m\n\u001b[0;32m     24\u001b[0m     \u001b[1;34m\"\"\"Load citation network dataset (cora only for now)\"\"\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     25\u001b[0m     \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'Loading {} dataset...'\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdataset\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 26\u001b[1;33m     idx_features_labels = np.genfromtxt(\"{}{}/\".format(path, dataset),# 读取节点标签\n\u001b[0m\u001b[0;32m     27\u001b[0m                                         dtype=np.dtype(str))\n\u001b[0;32m     28\u001b[0m     \u001b[0mfeatures\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0msp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcsr_matrix\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0midx_features_labels\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfloat32\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# 读取节点特征\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\anaconda3\\lib\\site-packages\\numpy\\lib\\npyio.py\u001b[0m in \u001b[0;36mgenfromtxt\u001b[1;34m(fname, dtype, comments, delimiter, skip_header, skip_footer, converters, missing_values, filling_values, usecols, names, excludelist, deletechars, replace_space, autostrip, case_sensitive, defaultfmt, unpack, usemask, loose, invalid_raise, max_rows, encoding, like)\u001b[0m\n\u001b[0;32m   1811\u001b[0m             \u001b[0mfname\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mos_fspath\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfname\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1812\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfname\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstr\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1813\u001b[1;33m             \u001b[0mfid\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlib\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_datasource\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mopen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfname\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'rt'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mencoding\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mencoding\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1814\u001b[0m             \u001b[0mfid_ctx\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mcontextlib\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mclosing\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfid\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1815\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\anaconda3\\lib\\site-packages\\numpy\\lib\\_datasource.py\u001b[0m in \u001b[0;36mopen\u001b[1;34m(path, mode, destpath, encoding, newline)\u001b[0m\n\u001b[0;32m    191\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    192\u001b[0m     \u001b[0mds\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mDataSource\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdestpath\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 193\u001b[1;33m     \u001b[1;32mreturn\u001b[0m \u001b[0mds\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mopen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mpath\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mencoding\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mencoding\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mnewline\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mnewline\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    194\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    195\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\anaconda3\\lib\\site-packages\\numpy\\lib\\_datasource.py\u001b[0m in \u001b[0;36mopen\u001b[1;34m(self, path, mode, encoding, newline)\u001b[0m\n\u001b[0;32m    527\u001b[0m             \u001b[1;32mif\u001b[0m \u001b[0mext\u001b[0m \u001b[1;33m==\u001b[0m \u001b[1;34m'bz2'\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    528\u001b[0m                 \u001b[0mmode\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mreplace\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"+\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 529\u001b[1;33m             return _file_openers[ext](found, mode=mode,\n\u001b[0m\u001b[0;32m    530\u001b[0m                                       encoding=encoding, newline=newline)\n\u001b[0;32m    531\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mPermissionError\u001b[0m: [Errno 13] Permission denied: './cora/'"
     ]
    }
   ],
   "source": [
    "def encode_onehot(labels):\n",
    "    classes = set(labels)\n",
    "    classes_dict = {c: np.identity(len(classes))[i, :] for i, c in enumerate(classes)}\n",
    "    labels_onehot = np.array(list(map(classes_dict.get, labels)), dtype=np.int32)\n",
    "    return labels_onehot\n",
    "\n",
    "def normalize(mx):\n",
    "    rowsum = np.array(mx.sum(1))\n",
    "    r_inv = np.power(rowsum, -1).flatten()\n",
    "    r_inv[np.isinf(r_inv)] = 0.\n",
    "    r_mat_inv = sp.diags(r_inv)\n",
    "    mx = r_mat_inv.dot(mx)\n",
    "    return mx\n",
    "\n",
    "def sparse_mx_to_torch_sparse_tensor(sparse_mx):\n",
    "    sparse_mx = sparse_mx.tocoo().astype(np.float32)\n",
    "    indices = torch.from_numpy(\n",
    "        np.vstack((sparse_mx.row, sparse_mx.col)).astype(np.int64))\n",
    "    values = torch.from_numpy(sparse_mx.data)\n",
    "    shape = torch.Size(sparse_mx.shape)\n",
    "    return torch.sparse.FloatTensor(indices, values, shape)\n",
    "\n",
    "def  load_data(path=\"./\", dataset=\"cora\"):\n",
    "    \"\"\"Load citation network dataset (cora only for now)\"\"\"\n",
    "    print('Loading {} dataset...'.format(dataset))\n",
    "    idx_features_labels = np.genfromtxt(\"{}{}/\".format(path, dataset),# 读取节点标签\n",
    "                                        dtype=np.dtype(str))\n",
    "    features = sp.csr_matrix(idx_features_labels[:, 1:-1], dtype=np.float32) # 读取节点特征\n",
    "    labels = encode_onehot(idx_features_labels[:, -1])                       # 标签用onehot方式表示\n",
    "    idx = np.array(idx_features_labels[:, 0], dtype=np.int32)                \n",
    "    idx_map = {j: i for i, j in enumerate(idx)}\n",
    "    edges_unordered = np.genfromtxt(\"{}{}.cites\".format(path, dataset),      # 读取边信息\n",
    "                                    dtype=np.int32)\n",
    "    edges = np.array(list(map(idx_map.get, edges_unordered.flatten())),\n",
    "                     dtype=np.int32).reshape(edges_unordered.shape)\n",
    "    adj = sp.coo_matrix((np.ones(edges.shape[0]), (edges[:, 0], edges[:, 1])),\n",
    "                        shape=(labels.shape[0], labels.shape[0]),\n",
    "                        dtype=np.float32)\n",
    "    adj = adj + adj.T.multiply(adj.T > adj) - adj.multiply(adj.T > adj)\n",
    "    features = normalize(features)                                            # 特征值归一化          \n",
    "    adj = normalize(adj + sp.eye(adj.shape[0]))                               # 边信息归一化\n",
    "\n",
    "    idx_train = range(140)                                                    # 训练集\n",
    "    idx_val = range(200, 500)                                                 # 验证集\n",
    "    idx_test = range(500, 1500)                                               # 测试集\n",
    "\n",
    "    features = torch.FloatTensor(np.array(features.todense()))\n",
    "    labels = torch.LongTensor(np.where(labels)[1])\n",
    "    adj = sparse_mx_to_torch_sparse_tensor(adj)                               # 转换成邻居矩阵\n",
    "\n",
    "    idx_train = torch.LongTensor(idx_train)\n",
    "    idx_val = torch.LongTensor(idx_val)\n",
    "    idx_test = torch.LongTensor(idx_test)\n",
    "                                                           \n",
    "    return adj, features, labels, idx_train, idx_val, idx_test            \n",
    "adj, features, labels, idx_train, idx_val, idx_test = load_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "backed-single",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'features' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-4-e2214c56c82c>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m     43\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mtorch\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnn\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfunctional\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlog_softmax\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mx\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdim\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m       \u001b[1;31m# 对每一个节点做softmax\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     44\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 45\u001b[1;33m model = GCN(nfeat=features.shape[1], nhid=hidden,\n\u001b[0m\u001b[0;32m     46\u001b[0m             nclass=labels.max().item() + 1,dropout=dropout)\n",
      "\u001b[1;31mNameError\u001b[0m: name 'features' is not defined"
     ]
    }
   ],
   "source": [
    "class GraphConvolution(Module):                            \n",
    "    def __init__(self, in_features, out_features, bias=True):\n",
    "        super(GraphConvolution, self).__init__()\n",
    "        self.in_features = in_features\n",
    "        self.out_features = out_features\n",
    "        self.weight = Parameter(torch.FloatTensor(in_features, out_features))\n",
    "        if bias:\n",
    "            self.bias = Parameter(torch.FloatTensor(out_features))\n",
    "        else:\n",
    "            self.register_parameter('bias', None)\n",
    "        self.reset_parameters()\n",
    "\n",
    "    def reset_parameters(self):\n",
    "        stdv = 1. / math.sqrt(self.weight.size(1))\n",
    "        self.weight.data.uniform_(-stdv, stdv)\n",
    "        if self.bias is not None:\n",
    "            self.bias.data.uniform_(-stdv, stdv)\n",
    "\n",
    "    def forward(self, input, adj):             # 这里代码做了简化如 3.2节。\n",
    "        support = torch.mm(input, self.weight) # (2708, 16) = (2708, 1433) X (1433, 16)\n",
    "        output = torch.spmm(adj, support)      # (2708, 16) = (2708, 2708) X (2708, 16)\n",
    "        if self.bias is not None:\n",
    "            return output + self.bias          # 加上偏置 (2708, 16)\n",
    "        else:\n",
    "            return output                      # (2708, 16)\n",
    "\n",
    "    def __repr__(self):\n",
    "        return self.__class__.__name__ + ' (' \\\n",
    "               + str(self.in_features) + ' -> ' \\\n",
    "               + str(self.out_features) + ')'\n",
    "\n",
    "class GCN(nn.Module):                                             # 定义两层GCN\n",
    "    def __init__(self, nfeat, nhid, nclass, dropout):\n",
    "        super(GCN, self).__init__()\n",
    "        self.gc1 = GraphConvolution(nfeat, nhid)\n",
    "        self.gc2 = GraphConvolution(nhid, nclass)\n",
    "        self.dropout = dropout\n",
    "\n",
    "    def forward(self, x, adj):\n",
    "        x = torch.nn.functional.relu(self.gc1(x, adj))\n",
    "        x = torch.nn.functional.dropout(x, self.dropout, training=self.training)\n",
    "        x = self.gc2(x, adj)\n",
    "        return torch.nn.functional.log_softmax(x, dim=1)       # 对每一个节点做softmax\n",
    "\n",
    "model = GCN(nfeat=features.shape[1], nhid=hidden,\n",
    "            nclass=labels.max().item() + 1,dropout=dropout)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "southeast-marijuana",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'Data' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-6-374bca065a47>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mclass\u001b[0m \u001b[0mPairData\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mData\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      2\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0medge_index_s\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mx_s\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0medge_index_t\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mx_t\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m         \u001b[0msuper\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mPairData\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0medge_index_s\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0medge_index_s\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mx_s\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mx_s\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mNameError\u001b[0m: name 'Data' is not defined"
     ]
    }
   ],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "directed-albert",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
